/* DynamicArray.c : functions to manipulate dynamic arrays */

/* NOTE: in this dynamic array, the elements are not copied: pointers
   to them are. If you wish to store a copy of something, you must
   copy it yourself. Storing things allocated on the stack is also
   likely to cause problems. */

#include "DynamicArray.h"

/* NewArray : make a new dynamic array with initial length of
   len elements, each size bytes long */
D_Array NewArray( unsigned int len )
{

  D_Array retval = NULL ;

  /* Allocate the struct holding our housekeeping information, and die
     on failure */
  if( !( retval = malloc( sizeof( struct dynarray ) ) ) )
    return( NULL ) ;

  /* Allocate the initial element pointers, and die on failure */
  if( !( retval -> array = malloc( sizeof( void * ) * len ) ) )
    {

      free( retval ) ;
      return( NULL ) ;

    }

  /* Store the length of the array */
  retval -> max_length = retval -> length = len ;

  /* Return what we have just allocated */
  return( retval ) ;

}

/* FreeArray : free a dynamic array */
int FreeArray( D_Array array )
{

  /* Free the element pointers */
  free( array -> array ) ;
  /* Free the struct */
  free( array ) ;

  /* This function only returns int for symmetry with everything else:
     free(3) never indicates errors, so we can't either. */
  return( 0 ) ;

}

/* AppendElement : append an element to the array; element is a
   pointer to the element, which will be copied into the array */
int AppendElement( D_Array array , void *element )
{

  /* Allocate space for the new element pointer, if we need to, or die
     on failure, indicating why. */
  if( array -> max_length <= array -> length )
    {

      if( !( array -> array = realloc( array -> array , 
				       sizeof( void * ) * 
				       ( array -> length + 1 ) ) ) )
	return( -errno ) ;

      array -> max_length++ ;

    }

  /* Copy the pointer */
  array -> array[ array -> length ] = element ;
  /* Store the new length */
  array -> length++ ;

  /* Success, so return so */
  return( 0 ) ;

}

/* InsertElement : insert an element to the array at position n. */
int InsertElement( D_Array array , unsigned int n , void *element )
{

  /* We will copy the element pointers into this new array, inserting
     the new one at the right moment. */
  void **newarray = NULL ;

  /* Allocate the new array, or die on failure */
  if( !( newarray = malloc( ( array -> length + 1 ) * sizeof( void * ) ) ) )
    return( -errno ) ;

  /* Copy the part below where the new element will be */
  memcpy( newarray , array -> array , n * sizeof( void * ) ) ;

  /* Copy the new element */
  newarray[ n ] = element ;

  /* Copy the rest */
  memcpy( newarray + n + 1 , 
	  array -> array + n , 
	  ( array -> length - n ) * sizeof( void * ) ) ;

  /* Free the old array, and copy the new one in its place */
  free( array -> array ) ;
  array -> array = newarray ;

  /* Update the length of the array */
  array -> max_length = array -> length++ ;

  /* Success, so return so */
  return( 0 ) ;

}

/* RemoveElement : remove the element at position n from the array. */
int RemoveElement( D_Array array , unsigned int n )
{

  /* We will copy the element pointers into this new array, except the
     one we don't want. */
  void **newarray = NULL ;

  /* See if the element being removed is beyond the end of the array:
     if so, do nothing. */
  if( n >= array -> length )
    return( 0 ) ;

  /* Allocate the new array, or die on failure */
  if( !( newarray = malloc( ( array -> length ) * sizeof( void * ) ) ) )
    return( -errno ) ;

  /* Copy the part below the element to be removed */
  memcpy( newarray , array -> array , n * sizeof( void * ) ) ;

  /* Ignore the element to be removed, and copy the rest */
  memcpy( newarray + n , 
	  array -> array + n + 1 , 
	  ( array -> length - ( n + 1 ) ) * sizeof( void * ) ) ;

  /* Free the old array, and copy the new one in its place */
  free( array -> array ) ;
  array -> array = newarray ;

  /* Store the reduced length */
  array -> max_length = array -> length-- ;

  /* Success, so return so */
  return( 0 ) ;

}

/* SetElement : set the element at position n to element */
int SetElement( D_Array array , unsigned int n , void *element )
{

  /* Grow the array if we need to, dying if we can't, and updating the
     length. */
  if( n > array -> max_length )
    {

      if( !( array -> array = realloc( array -> array , n + 1 ) ) )
	return( -errno ) ;

      array -> max_length = array -> length = n + 1 ;

    }

  /* Copy the element into the right place */
  array -> array[ n ] = element ;

  /* Success, so return so */
  return( 0 ) ;

}

/* GetElement: return the element at position n */
void *GetElement( D_Array array , unsigned int n )
{

  if( n >= array -> length )
    return( NULL ) ;
  else
    return array -> array[ n ] ;

}

/* GetLength: return the length of an array */
unsigned int GetLength( D_Array array )
{

  return( array -> length ) ;

}
